#include "GenericDBFile.h"
#include "DBFile.h"

SortedDBFile::SortedDBFile(SortInfo *info){
    sortInfo = info;
    buffsz = 100;
    queryBuilt=false;
    sortOrderExists=true;
    bigq=NULL;
    pipecount = 0;
    curPageInFile = 0;
    in = new Pipe(buffsz);
    out = new Pipe(buffsz);
}

int SortedDBFile::Create(char* f_path){
    reading = true;
    return GenericDBFile::Create(f_path);
}

int SortedDBFile::Open(char* f_path){
    reading = true;
    file_path = f_path;
    return GenericDBFile::Open(f_path);
}

void SortedDBFile::InitialBigQ(){
    bigq=new BigQ(*in, *out,*(sortInfo->myOrder),sortInfo->runLength);
}

void SortedDBFile::Load(Schema &f_schema, char *loadpath) {

    FILE* inputFile = fopen(loadpath,"r");
    Record temp;
    // whichPage is not neccesary
    off_t whichPage = 0;
    while(temp.SuckNextRecord(&f_schema, inputFile) == 1){
        Add(temp);
    }
    fclose(inputFile);
}

void SortedDBFile :: Add (Record &addme) {
    if(reading==true){
        if(!bigq){
            InitialBigQ();
        }
        reading = false;
    }
    in->Insert(&addme);
}

int SortedDBFile ::GetNextQuery(Record &fetchme, CNF &cnf, Record &literal){

    ComparisonEngine ceng;
    Record temp;
    while(1){
    if(dbPage.GetFirst(&temp)==1 ){
        if(ceng.Compare (&literal, query, &temp,sortInfo->myOrder) ==0){
            if (ceng.Compare (&temp, &literal, &cnf)){
                fetchme.Consume(&temp);
                return 1;
            }
        }
        else
            return 0;
    }
    else{
        //curPageInFile++;
        if(curPageInFile<dbFile.GetLength()-1)
            dbFile.GetPage(&dbPage,curPageInFile++);
        else
            return 0;
        }
    }
}

int SortedDBFile::SequentialGetNext (Record &fetchme, CNF &cnf, Record &literal) {

    ComparisonEngine comp;
    Record temp;
    while(1)
    {
        if(dbPage.GetFirst(&temp)==1 )
        {
            if (comp.Compare (&temp, &literal, &cnf)){
                fetchme.Consume(&temp);
                return 1;
            }
        }
        else
        {
            //curPageInFile++;
            if(curPageInFile<dbFile.GetLength()-1)
                dbFile.GetPage(&dbPage,curPageInFile++);
            else
                return 0;
        }
    }

 }

 int SortedDBFile::GetNext (Record &fetchme) {
    if(reading==false){
        Merge();
    }
    if(dbPage.GetFirst(&curRecord) == 1) {
        fetchme.Consume(&curRecord);
        return 1;
    }
    else {
        if(curPageInFile >= dbFile.GetLength() - 1) {
            return 0;
        }
        else {
            dbPage.EmptyItOut();
            dbFile.GetPage(&dbPage, curPageInFile++);
            dbPage.GetFirst(&curRecord);
            fetchme.Consume(&curRecord);
            return 1;
        }
    }
}

 int SortedDBFile::GetNext (Record &fetchme, CNF &cnf, Record &literal){
    OrderMaker *cnfOrder=new OrderMaker;
    OrderMaker *copySortOrder=new OrderMaker(sortInfo->myOrder->numAtts,(sortInfo->myOrder->whichAtts),(sortInfo->myOrder->whichTypes));
    if(reading==false){
        in->ShutDown();
        reading = true;
        Merge();
        queryBuilt=false;
        sortOrderExists=true;
    }
    if(sortOrderExists){
        if(!queryBuilt){
            queryBuilt=true;
            int search;
            query=new OrderMaker;
            bool isSameOrder = false;
            cnf.GetSortOrders(*cnfOrder,*copySortOrder);
            if(BuildQuery(cnfOrder,(sortInfo->myOrder))){
                isSameOrder = cnfOrder->isSameOrderMaker(copySortOrder);
                if(isSameOrder) {//Handle sequential search
                    sortOrderExists=false;
                    delete cnfOrder;
                    delete copySortOrder;
                    return SequentialGetNext(fetchme, cnf, literal);
                }
                search=BinarySearch(fetchme,cnf,literal);
                ComparisonEngine ceng;
                if(search){
                    if (ceng.Compare (&fetchme, &literal, &cnf)){
                        delete cnfOrder;
                        delete copySortOrder;
                        return 1;
                    }
                    else{
                        GetNextQuery(fetchme, cnf, literal);
                    }
                }
                else{
                    delete cnfOrder;
                    delete copySortOrder;
                    return 0;
                }
            }
            else{//Handle sequential search
                sortOrderExists=false;
                delete cnfOrder;
                delete copySortOrder;
                return SequentialGetNext(fetchme, cnf, literal);
            }
        }
        else{
            GetNextQuery(fetchme, cnf, literal);
        }
    }
    else{
        delete cnfOrder;
        delete copySortOrder;
        return SequentialGetNext(fetchme, cnf, literal);
    }
 }

 int SortedDBFile::BinarySearch(Record& fetchme,CNF &cnf,Record &literal){

    off_t first=curPageInFile;
    off_t last=dbFile.GetLength()-2;

    Page *midPage=new Page;
    ComparisonEngine ceng;
    off_t mid=first;
    bool found=false;
    dbFile.GetPage(midPage,last-2);

    while(first < last){
        mid=(first+last)/2;
        dbFile.GetPage(midPage,mid);
        if(midPage->GetFirst(&fetchme)==1 ){
            if (ceng.Compare (&literal, query, &fetchme,sortInfo->myOrder) <= 0){
                last = mid;
            }
            else{
                first = mid;
                if(first == last -1){                                //deal with last two, because mid can not get to the last one
                    dbFile.GetPage(midPage,last);
                    midPage->GetFirst(&fetchme);
                    if (ceng.Compare (&literal, query, &fetchme,sortInfo->myOrder) > 0){
                        mid=last;
                    }
                    break;
                }
            }
        }
        else
            break;
    }
    if(mid==curPageInFile){
        dbFile.GetPage(&dbPage,mid);
        while(dbPage.GetFirst(&fetchme)==1){
            if( ceng.Compare (&literal, query, &fetchme,sortInfo->myOrder) == 0 ){
                found=true;
                curPageInFile=mid+1;
                break;
            }
        }
    }
    else{
        dbFile.GetPage(&dbPage,last);
        while(dbPage.GetFirst(&fetchme)==1){
            if( ceng.Compare (&literal, query, &fetchme,sortInfo->myOrder) == 0 ){
                found=true;
                curPageInFile=last;
                break;
            }
        }
    }
    if(!found && mid < dbFile.GetLength()-2){
        dbFile.GetPage(&dbPage,mid);
        if(dbPage.GetFirst(&fetchme)==1 && ceng.Compare (&literal, query, &fetchme,sortInfo->myOrder) == 0){
            found=true;
            curPageInFile=mid+1;
        }
    }

    delete midPage;
    if(!found)
        return 0;
    else
        return 1;
}

 int SortedDBFile:: BuildQuery(OrderMaker *cnfOrder, OrderMaker *sortOrder){
    int numAtts=0;
    int j;
    int whichAtts[cnfOrder->numAtts];
    Type whichTypes[cnfOrder->numAtts];
    for(int i=0;i<sortOrder->numAtts;i++){
        for(j=0;j<cnfOrder->numAtts;j++){
            if(cnfOrder->whichAtts[j]==sortOrder->whichAtts[i]){
                whichAtts[numAtts]=numAtts;
                whichTypes[numAtts]=sortOrder->whichTypes[i];
                numAtts++;
                break;
            }

        }

        if(j==cnfOrder->numAtts){
            break;
        }
    }
    query = new OrderMaker(numAtts, whichAtts, whichTypes);
    query->Print();
    if(numAtts>0)
        return 1;
    else
        return 0;
}


int SortedDBFile::Merge(){

    in->ShutDown();
    reading=true;
    if(dbFile.GetLength()!=0){
        MoveFirst();
     }
    Record *fromFile=new Record;
    Record *fromPipe=new Record;
    DBFile tempFile;
    tempFile.Create("./binaries/heap.tmp",heap,NULL);
    int fcount=0,pcount=0,totalcount=0;

    int fp=out->Remove (fromPipe);
    int ff=GetNext(*fromFile);

    ComparisonEngine ceng;

    while (1) {
        if (fp && ff) {
            if (ceng.Compare (fromPipe, fromFile, sortInfo->myOrder) >= 1) {
                tempFile.Add(*fromFile);
                ++fcount;
                ff=GetNext(*fromFile);
            }
            else{
                tempFile.Add(*fromPipe);
                ++pcount;
                fp=out->Remove(fromPipe);
            }
        }
        else{
 //           dbFile.AddPage(&dbPage, dbFile.GetLength()-1);
 //           dbPage.EmptyItOut();
            break;
        }
    }
    while(ff){
        tempFile.Add(*fromFile);
        ++fcount;
        ff=GetNext(*fromFile);
    }
    while(fp){
        tempFile.Add(*fromPipe);
        ++pcount;
        fp=out->Remove (fromPipe);
    }
    tempFile.AdditionAddPage();
    cout<<"File Count :"<<fcount;
    cout<<endl;
    cout<<"Pipe Count :"<<pcount;
    delete fromFile;
    delete fromPipe;
    delete in;
    delete out;
    delete bigq;

    tempFile.Close();

    remove (file_path.c_str());
    remove("./binaries/heap.tmp.md");
    remove("midFile.bin");
    rename("./binaries/heap.tmp",file_path.c_str());
    remove("./binaries/heap.tmp");
}

int SortedDBFile :: Close () {
    if(reading==false){
        Merge();
    }
    if(dbFile.Close() == 0)
        return 0;
    else
        return 1;
}

void SortedDBFile :: AdditionAddPage(){

}

void SortedDBFile :: MoveFirst () {
    if(reading==false){
        Merge();
    }
 /*   curPageInFile = 1;
    if(dbFile.GetFilDes() <= 0)
        return;
    else
        dbFile.GetPage(&dbPage,curPageInFile);
    queryBuilt=false;
    sortOrderExists=true;*/
    if(dbFile.GetFilDes() < 0)
        return;
    curPageInFile = 0;
}

SortedDBFile::~SortedDBFile(){
    delete sortInfo->myOrder;
    delete sortInfo;
    delete query;
}
